//
//  Parser.cpp
//  Solver
//
//  Created by Nicolas Winkler on 14.07.13.
//  Copyright (c) 2013 Nicolas Winkler. All rights reserved.
//

#include "Parser.h"
#include "Tokenizer.h"
#include "OperationExpression.h"
#include "Definitions.h"

#include <iostream>
#include <cstdlib>

using namespace solver;


std::string Parser::operators[] = {
		"+", "-", "*", "/", "^", "&", "|", "xor"
};


Parser::Parser(const solver::Tokenizer& tokenizer) :
tokenizer(&tokenizer)
{
    index = 0;
}


Parser::~Parser(void)
{
}


void Parser::run(void)
{
#ifdef DEBUG_MESSAGES
	std::cout << "start of Parser::run\n";
#endif
	TermTokenBlock* mainBlock = createTokenBlock(0, tokenizer->getNTokens() - 1);
	Expression* ex =
			TermTemplate::getBaseTermTemplate().createTermExpression(*mainBlock);
	delete mainBlock;

#ifdef DEBUG_MESSAGES
	std::cout << "expression parsed\n";
	std::cout << "expression: " << ex->toString() << "\n";
#endif
	const Expression* eval = ex->evaluate();
	std::cout << eval->getAsString() << "\n";
#ifdef DEBUG_MESSAGES
	std::cout << "expression evaluated\n";
#endif
}


TermTokenBlock* Parser::createTokenBlock(int beginIndex, int endIndex)
{
	using std::string;
	TermTokenBlock* ttb = new TermTokenBlock();

	string oper;
	string functionName;
	for (int i = beginIndex; i <= endIndex; i++) {
		const string& token = tokenizer->getToken(i).token;
		if (Parser::isOperator(token)) {
			oper = token;
		}
		else if (token == "(") {
			int matchingIndex = findMatchingParanthesis(i);
			if (matchingIndex > -1) {
				if (functionName != "" && oper == "") {
					FunctionTokenBlock* ftblock = 0;
					functionName = "";
				}
				else {
					TermTokenBlock* subBlock = createTokenBlock(i + 1, matchingIndex);
					ttb->addBlock(oper, subBlock);
				}
				i = matchingIndex;
			}
			else
				throw ParserException("paranthesis without match");
		}
		else if (Parser::isNumeric(token)) {
			StringTokenBlock* stb = new StringTokenBlock(token);
			ttb->addBlock(oper, stb);
		}
		else if (i < endIndex &&
				tokenizer->getToken(i + 1).token == "(") {
			functionName = token;
			oper = "";
		}
	}
#ifdef DEBUG_MESSAGES
	std::cout << ttb->toString();
#endif
	return ttb;
}


int Parser::findMatchingParanthesis(int startIndex) const
{
	const std::string& token = tokenizer->getToken(startIndex).token;
	std::string matching;
	if (token == "(")
		matching = ")";
	else if (token == "[")
		matching = "]";
	else if (token == "{")
		matching = "}";
	else
		return -1;


	int parantLevel = 1;
	for (unsigned int i = startIndex + 1; i < tokenizer->getNTokens(); i++) {
		const std::string& token = tokenizer->getToken(i).token;
		if (token == "(") {
			parantLevel++;
		}
		if (token == ")") {
			parantLevel--;
		}
		if (parantLevel <= 0)
			return i;
	}
	return -1;
}



bool Parser::isNumeric(const std::string &token)
{
    for (unsigned int i = 0; i < token.length(); i++) {
        char a = token[i];
        if (a < 48 || a > 57)
            return false;
    }
    return true;
}


bool Parser::isOperator(const std::string &token)
{
	for (unsigned int i = 0; i < sizeof(operators) / sizeof(std::string); i++) {
		if (token == operators[i])
			return true;
	}
	return false;
}


ParserException::ParserException(const std::string& msg) :
Exception("ParserException: " + msg)
{
}


TokenBlock::TokenBlock(void)
{
}


TokenBlock::~TokenBlock(void)
{
}


StringTokenBlock::StringTokenBlock(const std::string& token)
{
	this->token = token;
}


const std::string& StringTokenBlock::getToken(void) const
{
	return this->token;
}


std::string StringTokenBlock::toString(void) const
{
	return "StringTokenBlock[" + this->token + "]";
}


Operation::Operation(const std::string& op, const TokenBlock* operand)
{
	this->operatorToken = op;
	this->operand = operand;
}


TermTokenBlock::TermTokenBlock(void)
{
}


TermTokenBlock::TermTokenBlock(const std::vector<Operation>& operations)
{
	this->operations = operations;
}


void TermTokenBlock::addBlock(const std::string& op, const TokenBlock* operand)
{
	operations.push_back(Operation(op, operand));
}


unsigned int TermTokenBlock::getNOperations(void) const
{
	return operations.size();
}


const Operation& TermTokenBlock::getOperation(unsigned int index) const
{
	return operations[index];
}


std::string TermTokenBlock::toString(void) const
{
	std::string ret;
	for (unsigned int i = 0; i < operations.size(); i++) {
		ret += "[" + operations[i].operatorToken + ", ";
		ret += operations[i].operand->toString() + "], ";
	}
	return ret.substr(0, ret.length() - 2);
}


Expression* TermTokenBlock::createExpression(void) const
{
	return new AdditionExpression();
}


